﻿using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
/* W tym miejscu dołączamy biblioteki */
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectInput;
using D3D = Microsoft.DirectX.Direct3D;
using System;

namespace Mirrors
{
    public partial class D3DSceneForm : Form
    {
        private Microsoft.DirectX.Direct3D.Device device;
        private Microsoft.DirectX.DirectInput.Device keyb;

        private DXMesh floor;
        private DXMesh centerSphere;
        private DXMesh flyingTorus;
        private DXMesh flyingMonkey;
        private float torusAngle = 0;
        private float monkeyAngle = 0;

        private D3D.Effect effect;
        private Random r = new Random();

        private MyCamera camera;
        private Vector4 lightPosition;
        private Matrix lightworldViewProjection;
        private int zoom;

        private float maxDepth = 5f;

        private RenderToSurface renderToSurface;
        private Texture shadowMapTexture;
        private int shadowMapSize = 512;


        public D3DSceneForm()
        {
            InitializeComponent();
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
        }

        public void InitializeDevice()
        {
            PresentParameters presentParams = new PresentParameters();
            presentParams.AutoDepthStencilFormat = DepthFormat.D16;
            presentParams.EnableAutoDepthStencil = true;
            presentParams.Windowed = true;
            presentParams.SwapEffect = SwapEffect.Discard;

            device = new Microsoft.DirectX.Direct3D.Device(0, Microsoft.DirectX.Direct3D.DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);
            effect = D3D.Effect.FromFile(device, "2.fx", null, ShaderFlags.Debug, null);

            renderToSurface = new RenderToSurface(device, shadowMapSize, shadowMapSize, Format.A8R8G8B8, true, DepthFormat.D16);
            shadowMapTexture = new Texture(device, shadowMapSize, shadowMapSize, 1, Usage.RenderTarget, Format.A8R8G8B8, Pool.Default);
        }

        public void InitializeKeyboard()
        {
            keyb = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
            keyb.SetCooperativeLevel(this, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);
            keyb.Acquire();
        }

        public void CameraPositioning()
        {
            zoom = 50;
            camera = new MyCamera(new Vector3(centerSphere.radius * zoom, centerSphere.radius * zoom, -centerSphere.radius * zoom), new Vector3(0, 1, 0));
            //camera = new MyCamera(new Vector3(0, 1, centerSphere.radius * zoom), new Vector3(0, 1, 0));
            //camera = new MyCamera(new Vector3(0, 50, 0), new Vector3(0, 0, 1));
            maxDepth = centerSphere.radius * 100f;
            device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1f, maxDepth);
            device.Transform.View = camera.ViewMatrix(new Vector3(0, 3, 0));
            device.RenderState.Lighting = false;
            device.Lights[0].Type = LightType.Directional;
            device.Lights[0].Diffuse = Color.White;
            device.Lights[0].Direction = new Vector3(-0.5f, 0, -1f);
            device.Lights[0].Enabled = true;
            device.RenderState.CullMode = Cull.None;
        }

        private void LightPositioning()
        {
            lightPosition = new Vector4(10, 10, 10, 1f);
            maxDepth = centerSphere.radius * 100f;
            lightworldViewProjection = Matrix.LookAtLH(new Vector3(lightPosition.X, lightPosition.Y,lightPosition.Z), new Vector3(0, 3, 0), new Vector3(0, 1, 0)) * Matrix.PerspectiveFovLH((float)Math.PI/2f, this.Width/this.Height, 1f, maxDepth);
        }

        private void UpdateCameraPosition()
        {
            device.Transform.View = camera.ViewMatrix(new Vector3(0, 3, 0));
        }

        private void LoadMeshes()
        {
            floor = new DXMesh(ref device);
            floor.LoadFromFile("floor.x");
            floor.CalculateBounds();
            floor.Normalize(Matrix.Translation(-floor.center) * Matrix.RotationX((float)Math.PI / 2f) * Matrix.Scaling(4, 4, 4));
            floor.NormalizeVB();
            floor.CalculateBounds();

            centerSphere = new DXMesh(ref device);
            centerSphere.LoadFromFile("sphere.x");
            centerSphere.Normalize(Matrix.Translation(0,3f,0));
            centerSphere.NormalizeVB();
            centerSphere.CalculateBounds();

            flyingTorus = new DXMesh(ref device);
            flyingTorus.LoadFromFile("torus.x");
            flyingTorus.CalculateBounds();
            flyingTorus.Normalize(Matrix.Identity);
            flyingTorus.NormalizeVB();

            flyingMonkey = new DXMesh(ref device);
            flyingMonkey.LoadFromFile("monkey_head.x");
            flyingMonkey.Normalize(Matrix.Identity);
            flyingMonkey.NormalizeVB();
        }

        private void ReadKeyboard()
        {
            KeyboardState keys = keyb.GetCurrentKeyboardState();
            Vector3 pos = camera.Position;

            if (keys[Key.LeftArrow])
            {
                pos.TransformCoordinate(Matrix.RotationY((float)-Math.PI / 180f));
            }
            if (keys[Key.RightArrow])
            {
                pos.TransformCoordinate(Matrix.RotationY((float)Math.PI / 180f));
            }
            if (keys[Key.UpArrow])
            {
                pos.TransformCoordinate(Matrix.RotationX((float)-Math.PI / 180f));
            }
            if (keys[Key.DownArrow])
            {
                pos.TransformCoordinate(Matrix.RotationX((float)Math.PI / 180f));
            }
            if (keys[Key.M])
            {
                pos.TransformCoordinate(Matrix.Translation(-Math.Sign(pos.X) * 1, -Math.Sign(pos.Y) * 1, -Math.Sign(pos.Z) * 1));
            }
            if (keys[Key.N])
            {
                pos.TransformCoordinate(Matrix.Translation(Math.Sign(pos.X)*1, Math.Sign(pos.Y)*1, Math.Sign(pos.Z)*1));
            }
            camera.Position = pos;
        }
        private void draw(DXMesh mesh, Matrix world)
        {
            effect.SetValue("xWorld", world);
            device.SetStreamSource(0, mesh.vb, 0);
            device.VertexDeclaration = mesh.vd;
            effect.CommitChanges();
            int passes = effect.Begin(0);
            for (int i = 0; i < passes; i++)
            {
                effect.BeginPass(i);
                mesh.Render(ref effect, null);
                effect.EndPass();
            }
            effect.End();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            UpdateCameraPosition();
            Surface surface = shadowMapTexture.GetSurfaceLevel(0);
            renderToSurface.BeginScene(surface);
            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkSlateBlue, 1.0f, 0);
            //device.BeginScene();
            effect.Technique = "ShadowMap";
            effect.SetValue("xView", device.Transform.View);
            effect.SetValue("xProjection", device.Transform.Projection);
            effect.SetValue("xLightPosition", lightPosition);
            effect.SetValue("xLightWorldViewProjection", lightworldViewProjection);
            effect.SetValue("xMaxDepth", 160f);
            effect.SetValue("eye", new float[] { camera.Position.X, camera.Position.Y, camera.Position.Z, 1 });

            effect.SetValue("ambientI", new float[] { 0.1f, 0.1f, 0.1f });
            effect.SetValue("diffuseI", new float[] { 0.0f, 0.0f, 1.0f });
            effect.SetValue("specularI", new float[] { 1.0f, 1.0f, 1.0f });

            effect.SetValue("ambientK", new float[] { 1.0f, 1.0f, 1.0f });
            effect.SetValue("diffuseK", new float[] { 0.6f, 0.6f, 0.6f });
            effect.SetValue("specularK", new float[] { 1.0f, 1.0f, 1.0f });

            effect.SetValue("ambientG", new float[] { 0.25f, 0.25f, 0.25f });

            effect.SetValue("alpha", 50f);

            draw(floor, Matrix.Identity);
            draw(centerSphere, Matrix.Identity);
            float x = (float)(6 * Math.Cos(torusAngle * Math.PI / 180f));
            float z = (float)(3 * Math.Sin(torusAngle * Math.PI / 180f));
            Matrix world = Matrix.Translation(-flyingTorus.center) * Matrix.RotationX(torusAngle * 0.1f) * Matrix.RotationZ(torusAngle * 0.1f) *Matrix.Translation(x, 3.5f, z);
            draw(flyingTorus, world);

            x = (float)(5*Math.Cos(60*Math.PI/180f)*Math.Cos(monkeyAngle*Math.PI/180f));
            z = (float) (9*Math.Cos(60*Math.PI/180f)*Math.Sin(monkeyAngle*Math.PI/180f));
            float y = (float)(3 * Math.Sin(60 * Math.PI / 180f));
            world = Matrix.Translation(-flyingMonkey.center) * Matrix.RotationX(monkeyAngle * 0.1f) * Matrix.RotationZ(monkeyAngle * 0.1f) * Matrix.Translation(x, y + 3.5f, z);
            draw(flyingMonkey, world);

            renderToSurface.EndScene(Filter.None);
            //TextureLoader.Save(@"D:\tex.bmp", ImageFileFormat.Bmp, shadowMapTexture);
            //device.EndScene();
            effect.Technique = "shadows";
            effect.SetValue("xShadowDepthMap", shadowMapTexture);
            effect.CommitChanges();
            device.BeginScene();
            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkSlateBlue, 1.0f, 0);
            draw(floor, Matrix.Identity);
            draw(centerSphere, Matrix.Identity);
            x = (float)(6 * Math.Cos(torusAngle * Math.PI / 180f));
            z = (float)(3 * Math.Sin(torusAngle * Math.PI / 180f));
            world = Matrix.Translation(-flyingTorus.center) * Matrix.RotationX(torusAngle * 0.1f) * Matrix.RotationZ(torusAngle * 0.1f) * Matrix.Translation(x, 3.5f, z);
            draw(flyingTorus, world);
            x = (float)(5 * Math.Cos(60 * Math.PI / 180f) * Math.Cos(monkeyAngle * Math.PI / 180f));
            z = (float)(9 * Math.Cos(60 * Math.PI / 180f) * Math.Sin(monkeyAngle * Math.PI / 180f));
            y = (float)(3 * Math.Sin(60 * Math.PI / 180f));
            world = Matrix.Translation(-flyingMonkey.center) * Matrix.RotationX(monkeyAngle * 0.1f) * Matrix.RotationZ(monkeyAngle * 0.1f) * Matrix.Translation(x, y + 3.5f, z);
            draw(flyingMonkey, world);
            device.EndScene();

            device.Present();
            this.Invalidate();
            //TextureLoader.Save(@"D:\tex.bmp", ImageFileFormat.Bmp, shadowMapTexture);
            ReadKeyboard();
            torusAngle += 0.3f;
            monkeyAngle += 0.5f;
        }



        public static void Main()
        {
            using (D3DSceneForm dxForm = new D3DSceneForm())
            {
                dxForm.InitializeDevice();
                dxForm.InitializeKeyboard();
                dxForm.LoadMeshes();
                dxForm.CameraPositioning();
                dxForm.LightPositioning();
                Application.Run(dxForm);
            }
        }

        

    }
}
